package com.powergisol.gis.service.impl;
import java.io.*;

import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.crypto.SecureUtil;
import com.mongodb.client.gridfs.GridFSBucket;
import com.mongodb.client.gridfs.GridFSBuckets;
import com.mongodb.client.gridfs.GridFSDownloadStream;
import com.mongodb.client.gridfs.model.GridFSFile;
import com.mongodb.client.gridfs.model.GridFSUploadOptions;
import com.mongodb.client.result.DeleteResult;
import com.powergisol.gis.entity.FileDocument;
import com.powergisol.gis.service.IFileService;
import com.powergisol.gis.util.FileList;
import org.bson.Document;
import org.bson.types.ObjectId;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Field;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.gridfs.GridFsOperations;
import org.springframework.data.mongodb.gridfs.GridFsResource;
import org.springframework.data.mongodb.gridfs.GridFsTemplate;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.zip.GZIPOutputStream;

import static java.lang.Integer.valueOf;


@Service
public class FileServiceImpl implements IFileService {
    private static Logger logger = LoggerFactory.getLogger(FileServiceImpl.class);

    @Autowired
    private MongoTemplate mongoTemplate;
    @Autowired
    private GridFsTemplate gridFsTemplate;
    @Autowired
    private GridFSBucket gridFSBucket;

    @Autowired
    private MongoDbFactory mongoDbFactory;
    @Autowired
    private GridFsOperations operations;

    @Autowired
    private RedisTemplate redisTemplate;

    @Value("${collectionName}")
    private String collectionName;

    public static void main(String[] args) {
       File file = new File("d:\\aa\bb\\cc.txt");
        String name = file.getName();
        String a=name.substring(0, name.indexOf("."));
        System.out.println(a);
    }

    @Override
    @Async
    public void saveDb(List<File> file,String uuid,String serviceName,String path,int i) {
        for (File files:file) {
            Integer savedNum = 0;
            if(redisTemplate.hasKey(uuid+"SavedFile"+i)){
                savedNum = (Integer)redisTemplate.opsForValue().get(uuid+"SavedFile"+i);
            }
            savedNum = savedNum+1;
            String key = uuid+files.getAbsolutePath().replace(path,"");
            //logger.info("存库key={}",key);
            String name = files.getName();
            String suffix = name.substring(name.lastIndexOf(".") + 1);
            fileToDb(files,key,suffix,uuid);
            redisTemplate.opsForValue().set(uuid+"SavedFile"+i,savedNum,24,TimeUnit.HOURS);
        }
    }

    @Override
    public String importFilesToFs(InputStream in, String name, String suffix, String serviceName){
        try {
            //if(null == in){
            in = new FileInputStream(new File("E:\\gd_sz_xzqh.sld"));
            //}
            String filename = SecureUtil.md5(name+serviceName);
            FileDocument fileDocument = new FileDocument();
            fileDocument.setServiceName(serviceName);
            fileDocument.setUploadDate(new Date());
            fileDocument.setMd5(filename);
            fileDocument.setSuffix(suffix);
           // fileDocument.setName(filename);
            //设置chunks长度为358400字节，如果文件过大则创建新的分块
            // 自定义的数据放在metadata里
            GridFSUploadOptions options = new GridFSUploadOptions().chunkSizeBytes(358400);
            ObjectId fileId = gridFSBucket.uploadFromStream(filename, in, options);
            fileDocument.setGridfsId(fileId.toString());
            mongoTemplate.save(fileDocument , collectionName);
            return fileId.toString();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    private String fileToDb(File file,String key,String suffix,String uuid){
        InputStream streamToUploadFrom =null;
        try {
            FileDocument fileDocument = new FileDocument();
            fileDocument.setServiceName(uuid);
            fileDocument.setUploadDate(new Date());
            fileDocument.setMd5(SecureUtil.md5(key));
            fileDocument.setSuffix(suffix);
            fileDocument.setName(file.getName());

            Document document = new Document();
            document.append("suffix", suffix);

            streamToUploadFrom = new FileInputStream(file);

            //设置chunks长度为358400字节，如果文件过大则创建新的分块
            // 自定义的数据放在metadata里
            GridFSUploadOptions options = new GridFSUploadOptions().chunkSizeBytes(358400).metadata(document);
            ObjectId fileId = gridFSBucket.uploadFromStream(SecureUtil.md5(key), streamToUploadFrom, options);
            //ObjectId fileId = gridFSBucket.uploadFromStream(SecureUtil.md5(key), bais, options);
            fileDocument.setGridfsId(fileId.toString());
            mongoTemplate.save(fileDocument , collectionName);
            return fileId.toString();
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            if(null != streamToUploadFrom){
                try {
                    streamToUploadFrom.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return null;
    }

    @Override
    @Async
    public String importFilesToFs(File file,String key,String suffix,String uuid) {
        return fileToDb(file,key,suffix,uuid);
    }

    @Override
    public String importFilesToFs(File file,String name,String suffix) {
        try {
                InputStream streamToUploadFrom = new FileInputStream(file);
                Document document = new Document();
                //自定义数据，放入文件真正名和文件类型
                document.append("fileTrueName", name);
                document.append("suffix", suffix);
                //设置chunks长度为358400字节，如果文件过大则创建新的分块
                // 自定义的数据放在metadata里
                GridFSUploadOptions options = new GridFSUploadOptions().chunkSizeBytes(358400).metadata(document);
                String filename = SecureUtil.md5(name);
                ObjectId fileId = gridFSBucket.uploadFromStream(filename, streamToUploadFrom, options);
                return fileId.toString();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 表单上传附件
     * @param md5
     * @param file
     * @return
     */
    @Override
    public FileDocument saveFile(String md5, MultipartFile file,String uniqFlag,String serviceName) {
        //已存在该文件，则实现秒传
        FileDocument fileDocument = getByMd5(md5);
        if(fileDocument != null){
            return fileDocument;
        }
        fileDocument = new FileDocument();
        fileDocument.setServiceName(serviceName);
        fileDocument.setName(null==uniqFlag?file.getOriginalFilename():uniqFlag);
        fileDocument.setSize(file.getSize());
        fileDocument.setContentType(file.getContentType());
        fileDocument.setUploadDate(new Date());
        fileDocument.setMd5(md5);
        String suffix = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf("."));
        fileDocument.setSuffix(suffix);
        try {
            String gridfsId = uploadFileToGridFS(file.getInputStream() , file.getContentType());
            fileDocument.setGridfsId(gridfsId);
            mongoTemplate.save(fileDocument , collectionName);
        }catch (IOException ex){
            ex.printStackTrace();
        }
        return fileDocument;
    }

    /**
     * 上传文件到Mongodb的GridFs中
     * @param in
     * @param contentType
     * @return
     */
    private String uploadFileToGridFS(InputStream in , String contentType){
        String gridfsId = IdUtil.simpleUUID();
        //文件，存储在GridFS中
        gridFsTemplate.store(in, gridfsId , contentType);
        return gridfsId;
    }

    /**
     * 删除附件
     * @param id 文件id
     * @param isDeleteFile 是否删除文件
     */
    @Override
    public void removeFile(String id, boolean isDeleteFile) {
        FileDocument fileDocument = mongoTemplate.findById(id , FileDocument.class , collectionName);
        if(fileDocument != null){
            Query query = new Query().addCriteria(Criteria.where("_id").is(id));
            DeleteResult result = mongoTemplate.remove(query , collectionName);
            System.out.println("result:" + result.getDeletedCount());

            if(isDeleteFile){
                Query deleteQuery = new Query().addCriteria(Criteria.where("filename").is(fileDocument.getGridfsId()));
                gridFsTemplate.delete(deleteQuery);
            }
        }
    }

    /**
     * 查询附件
     * @param id 文件id
     * @return
     * @throws IOException
     */
    @Override
    public Optional<FileDocument> getById(String id){
        FileDocument fileDocument = mongoTemplate.findById(id , FileDocument.class , collectionName);
        return getOption(fileDocument);
    }
    /**
     * 根据md5获取文件对象
     * @param md5
     * @return
     */
    @Override
    public FileDocument getByMd5(String md5) {
        Query query = new Query().addCriteria(Criteria.where("md5").is(md5));
        FileDocument fileDocument = mongoTemplate.findOne(query , FileDocument.class , collectionName);
        return fileDocument;
    }

    private Optional<FileDocument> getOption(FileDocument fileDocument){
        if(fileDocument != null){
            Query gridQuery = new Query().addCriteria(Criteria.where("filename").is(fileDocument.getMd5()));
            try {
                GridFSFile fsFile = gridFsTemplate.findOne(gridQuery);
                GridFSDownloadStream in = gridFSBucket.openDownloadStream(fsFile.getObjectId());
                if(in.getGridFSFile().getLength() > 0){
                    GridFsResource resource = new GridFsResource(fsFile, in);
                    InputStream inputStream  = resource.getInputStream();
                    fileDocument.setContent(IoUtil.readBytes(inputStream));
                    return Optional.of(fileDocument);
                }else {
                    return Optional.empty();
                }
            }catch (IOException ex){
                ex.printStackTrace();
            }
        }
        return Optional.empty();
    }

    @Override
    public Optional<FileDocument> getByName(String name) {
        Query query = new Query().addCriteria(Criteria.where("md5").is(name));
        FileDocument fileDocument = mongoTemplate.findOne(query , FileDocument.class , collectionName);
        return getOption(fileDocument);
    }

    @Override
    public List<FileDocument> getByServiceName(String serviceName) {
        Query query = new Query().addCriteria(Criteria.where("serviceName").is(serviceName));
        List<FileDocument> fileDocument = mongoTemplate.find(query , FileDocument.class , collectionName);
        return fileDocument;
    }

    @Override
    public File getFileByCondition(String serviceName, String suffixName,String url) {
        Query query = new Query().addCriteria(Criteria.where("serviceName").is(serviceName)).addCriteria(Criteria.where("suffix").is(suffixName));
        FileDocument fileDocument = mongoTemplate.findOne(query , FileDocument.class , collectionName);
        File file = new File(url+File.separator+fileDocument.getName());
        return file;
    }

    @Override
    public List<FileDocument> listFilesByPage(int pageIndex, int pageSize) {
        Query query = new Query().with(new Sort(Sort.Direction.DESC, "uploadDate"));
        long skip = (pageIndex -1) * pageSize;
        query.skip(skip);
        query.limit(pageSize);
        Field field = query.fields();
        field.exclude("content");
        List<FileDocument> files = mongoTemplate.find(query , FileDocument.class , collectionName);
        return files;
    }

    @Async
    @Override
    public void delByUuid(String uuid) {
        Query query = new Query().addCriteria(Criteria.where("serviceName").is(uuid));
     //   gridFsTemplate.delete(query);

        List<FileDocument> fileDocuments = mongoTemplate.find(query , FileDocument.class , collectionName);
        if(fileDocuments.size()>0){
            for (FileDocument fileDocument:fileDocuments) {
                try{
                    //删除集合
                    Query fsQuery = new Query().addCriteria(Criteria.where("_id").is(fileDocument.getId()));
                    mongoTemplate.remove(fsQuery , collectionName);

                    if(null != fileDocument.getGridfsId()){
                        gridFSBucket.delete(new ObjectId(fileDocument.getGridfsId()));
                    }
                    //删除文件
                    Query deleteQuery = new Query().addCriteria(Criteria.where("filename").is(fileDocument.getMd5()));
                    gridFsTemplate.delete(deleteQuery);
                }catch (Exception e){

                }

            }
        }
        System.out.println("删除完成");
    }
    @Override
    public boolean upByMD5(String uuid,String key,String object) {
        Boolean flag = false;
        String uuidkey = uuid+File.separator+key;
        String MD5 = SecureUtil.md5(uuidkey.replace("/", File.separator));
        FileDocument fileDocument = getByMd5(MD5);
        if(null != fileDocument){
            //删除集合
            Query fsQuery = new Query().addCriteria(Criteria.where("_id").is(fileDocument.getId()));
            mongoTemplate.remove(fsQuery , collectionName);
            //删除文件
            gridFSBucket.delete(new ObjectId(fileDocument.getGridfsId()));
            //删除文件
            Query deleteQuery = new Query().addCriteria(Criteria.where("filename").is(fileDocument.getMd5()));
            gridFsTemplate.delete(deleteQuery);

            //存库
            try {
                InputStream streamToUploadFrom = new ByteArrayInputStream(object.getBytes());
                FileDocument fileDocumentSave = new FileDocument();
                fileDocumentSave.setServiceName(uuid);
                fileDocumentSave.setUploadDate(new Date());
                fileDocumentSave.setMd5(MD5);
                fileDocumentSave.setSuffix("json");
                Document document = new Document();
                document.append("suffix", "json");
                //设置chunks长度为358400字节，如果文件过大则创建新的分块
                // 自定义的数据放在metadata里
                GridFSUploadOptions options = new GridFSUploadOptions().chunkSizeBytes(358400).metadata(document);
                ObjectId fileId = gridFSBucket.uploadFromStream(MD5, streamToUploadFrom, options);
                fileDocumentSave.setGridfsId(fileId.toString());
                mongoTemplate.save(fileDocumentSave , collectionName);
            } catch (Exception e) {
                e.printStackTrace();
                flag = false;
            }
        }
        return flag;
    }

    @Override
    @Async
    public void writeToDisk(String uuid,String path) {
        GridFSDownloadStream stream = null;
        path = path.substring(0, path.lastIndexOf(File.separator));
        Query query = new Query().addCriteria(Criteria.where("serviceName").is(uuid));
        List<FileDocument> fileDocuments = mongoTemplate.find(query , FileDocument.class , collectionName);
        if(fileDocuments.size()>0){
            for (FileDocument fileDocument:fileDocuments) {
                try{
                    GridFSFile gridFSFile = operations.findOne(new Query(Criteria.where("filename").is(fileDocument.getMd5())));
                    if(null == gridFSFile){
                        continue;
                    }
                    ObjectId objectId = gridFSFile.getObjectId();
                    stream = gridFSBucket.openDownloadStream(objectId);
                    /** chunk size */
                    int size = gridFSFile.getChunkSize();
                    if(null == stream){
                        continue;
                    }

                    File file = new File(path+File.separator + fileDocument.getName());
                    if(!file.getParentFile().exists()){
                        file.getParentFile().mkdirs();
                    }
                    if(!file.exists()){
                        FileOutputStream out = new FileOutputStream(file.getAbsoluteFile());
                        Long len = gridFSFile.getLength();
                        /** loop time */
                        BigDecimal num = new BigDecimal(len).divide(new BigDecimal(size),0,BigDecimal.ROUND_UP);
                        Integer cnt = valueOf(num.toString());
                        byte[] bts = new byte[size];
                        while (cnt-- > 0){
                            int tmp = stream.read(bts);
                            out.write(bts,0,tmp);
                        }
                        out.close();
                    }
                }catch (Exception e){

                }
            }
        }
    }

}
